package transactor

import (
	"encoding/hex"
	"hash"

	"github.com/golang/protobuf/proto"
	"git.cloud.inspur.com/ichain/ichain-protos-go/common"
	pb "git.cloud.inspur.com/ichain/ichain-protos-go/inode"
	contextApi "git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/context"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/ichain"
	"github.com/pkg/errors"
)

// computeTxnID
// @Description:  计算交易id
func computeTxnID(nonce, creator []byte, h hash.Hash) (string, error) {
	b := append(nonce, creator...)

	_, err := h.Write(b)
	if err != nil {
		return "", err
	}
	digest := h.Sum(nil)
	id := hex.EncodeToString(digest)

	return id, nil
}

// signProposal
// @Description:  提案签名
func signProposal(ctx contextApi.Client, proposal *pb.Proposal) (*pb.SignedProposal, error) {
	proposalBytes, err := proto.Marshal(proposal)
	if err != nil {
		return nil, errors.Wrap(err, "mashal proposal failed")
	}

	signingMgr := ctx.SigningManager()
	if signingMgr == nil {
		return nil, errors.New("signing manager is nil")
	}

	signature, err := signingMgr.Sign(proposalBytes, ctx.PrivateKey())
	if err != nil {
		return nil, errors.WithMessage(err, "sign failed")
	}

	return &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: signature}, nil
}

// getTargetsWithoutDuplicates 对目标节点去重
func getTargetsWithoutDuplicates(targets []ichain.Inode) []ichain.Inode {
	inodeUrlsToTargets := map[string]ichain.Inode{}
	var uniqueTargets []ichain.Inode

	// 对目标节点去重
	for _, inode := range targets {
		if inode == nil {
			continue
		}
		if _, present := inodeUrlsToTargets[inode.URL()]; !present {
			uniqueTargets = append(uniqueTargets, inode)
			inodeUrlsToTargets[inode.URL()] = inode
		}
	}

	if len(uniqueTargets) != len(targets) {
		logger.Warn("Duplicate target inodes in configuration")
	}

	return uniqueTargets
}

func validateProposalResponses(responses []*ichain.TransactionProposalResponse) error {
	for _, r := range responses {
		if r.ProposalResponse.Response.Status < int32(common.Status_SUCCESS) || r.ProposalResponse.Response.Status >= int32(common.Status_BAD_REQUEST) {
			return errors.Errorf("proposal response was not successful, error code %d, msg %s", r.ProposalResponse.Response.Status, r.ProposalResponse.Response.Message)
		}
	}
	return nil
}

// signPayload 为负责签名生成签名信封
func signPayload(ctx contextApi.Client, payload *common.Payload) (*ichain.SignedEnvelope, error) {
	payloadBytes, err := proto.Marshal(payload)
	if err != nil {
		return nil, errors.WithMessage(err, "marshaling of payload failed")
	}

	signingMgr := ctx.SigningManager()
	signature, err := signingMgr.Sign(payloadBytes, ctx.PrivateKey())
	if err != nil {
		return nil, errors.WithMessage(err, "signing of payload failed")
	}
	return &ichain.SignedEnvelope{Payload: payloadBytes, Signature: signature}, nil
}

// consensersFromChain
// @Description: 在sdk配置中读取共识节点
func consensersFromChain(ctx contextApi.Client, chainID string) ([]ichain.Consenser, error) {
	chNetworkConfig := ctx.EndpointConfig().ChainConfig(chainID)
	consensers := []ichain.Consenser{}
	for _, chConsenser := range chNetworkConfig.Consensers {
		// 读取共识节点配置
		consenserConfig, found, ignoreConsenser := ctx.EndpointConfig().ConsenserConfig(chConsenser)
		if !found || ignoreConsenser {
			continue
		}

		consenser, err := ctx.IChainProvider().CreateConsenserFromConfig(consenserConfig)
		if err != nil {
			return nil, errors.WithMessage(err, "failed to create consenser from config")
		}

		consensers = append(consensers, consenser)
	}
	return consensers, nil
}
